#define STRICT
#define WIN32_LEAN_AND_MEAN

#include "stdafx.h"
#include <windows.h>
#include <time.h>
#include "Scene.h"
#include "..\Render\Render.h"
#include "..\GUI\GUI.h"
#include "Viewer.h"
#include "Model.h"
#include "..\Window.h"

#include <vector>
#include "..\..\Global\globaldef.h"


Server CViewer::g_server;
CViewer::ViewerMode CViewer::Mode = CViewer::NAVIGATE_FREEVIEW;
CViewer::ViewerMode CViewer::LastMode = CViewer::NAVIGATE_FREEVIEW;

D3DXVECTOR3 g_CameraPosition = D3DXVECTOR3(0.0f,0.0f,0.0f);
float g_CameraAngles[] = {0.0f,0.0f};

float g_time = 0.0f;
StreamedMesh* g_SelectedObject = NULL;


void CViewer::Error(const char* format,...) {

	char buffer[512] = {'\0'};
	char final[512] = {'\0'};

	va_list	args; 
	va_start (args,format); 
	vsprintf_s(buffer, 512,format,args); 
	perror(buffer); 
	va_end(args); 

}

void CViewer::OnPacketNetwork(Connection::Packet* pPacket) {

	if(!pPacket)
		return;

	const int packetID = pPacket->header.packetID;
	if(packetID == NETWORK_PROBE) {
		//Handled by network library
	}
	
	if(packetID == NETWORK_DISCONNECT) {
		//Handled by network library
	}
}

void CViewer::OnPacketQuery(Connection::Packet* pPacket) {

	if(!pPacket)
		return;

	StreamedMesh *pSM = NULL;
	QuerysStructs::tViewerInfoQuery* pData = (QuerysStructs::tViewerInfoQuery*)pPacket->pData;
	const bool targetExists = Scene::GetGeometry(&pSM, &pData->target);
	const int packetID = pPacket->header.packetID;

	if(packetID == QUERY_RESPONCE_OBJECTEXISTS) {

		u_int responce = targetExists ? 1 : 0;
		CViewer::g_server.SendDataPacket((LPVOID)&responce, sizeof(responce), QUERY_RESPONCE_OBJECTEXISTS, 0);
	}

	if(packetID == QUERY_RESPONCE_GEOMETRY) {

//		Beep(50,100);
		QuerysStructs::tViewerGeometryInfoResponce responce = 
		{ 
			targetExists ? pSM->m_TriangleCount : 0 
		};
		CViewer::g_server.SendDataPacket((LPVOID)&responce, sizeof(responce), QUERY_RESPONCE_GEOMETRY, 0);
	}
	
	/*if(packetID == QUERY_RESPONCE_INSTAINCING) {

		const int childInstanceCount = targetExists ? pSM->ChildInstancingCount() : 0;

		QuerysStructs::tViewerInstanceInfoResponce responce = 
		{
			targetExists, 
			childInstanceCount, 
			tCharBuffer(), 
			tProceduralData()
		};

		if(childInstanceCount == 0) {
		
			CViewer::g_server.SendDataPacket((LPVOID)&responce, sizeof(responce), QUERY_RESPONCE_INSTAINCING, 0);
		}
		else {

			for(uint i = 0; i < childInstanceCount; i++) {

				StreamedMesh* pMesh = NULL;
				if(pSM->GetChildInstance(i, &pMesh, &responce.childProcedrual)) {
					responce.childMesh = pMesh->m_ID;
					CViewer::g_server.SendDataPacket((LPVOID)&responce, sizeof(responce), QUERY_RESPONCE_INSTAINCING, 0);
				}
			}
		}
	}*/

	if(packetID == QUERY_RESPONCE_TEXTURES) {

//	Beep(50,50);
		/*
		QuerysStructs::tViewerTextureInfoResponce responce =
		{
			{{'\0'}, {'\0'}, {'\0'}}
		};

		if(targetExists) {
			
			for(u_int i = 0; i < StreamedMesh::TEXTURE_COUNT; i++) {

				if(pSM->m_Textures[i]) 
					memcpy(&responce.textureFilenames[i], pSM->m_Textures[i]->filename.c_str(), 512);
			}
		}
		CViewer::g_server.SendDataPacket((LPVOID)&responce, sizeof(responce), QUERY_RESPONCE_TEXTURES, 0);*/
	}


	if(packetID == QUERY_RESPONCE_DEFORMATION) {

		QuerysStructs::tViewerDeformationInfoResponce responce =
		{
			0,0,0,
		};

		if(targetExists) {
			
			responce.isActive = pSM->m_Deformation;
//			responce.proceduralFormat = pSM->m_deformation.proceduralFormat;
			responce.scale = pSM->m_DeformationScale;
		}
		CViewer::g_server.SendDataPacket((LPVOID)&responce, sizeof(responce), QUERY_RESPONCE_DEFORMATION, 0);
	}

	if(packetID == QUERY_RESPONCE_PARAMETERS) {

		QuerysStructs::tViewerParameterInfoResponce responce =
		{
			{0}, 0, 0, 0,
		};

		if(targetExists) {
			
			if(pSM->m_Shader) {
				pSM->m_Shader->filename.copy(responce.shaderFilename, MAX_TEXTURE_BYTES);
				responce.shaderFeatureSet = pSM->m_Shader->features;
				responce.inputFeatureSet = pSM->m_Shader->inputFormat;
			}


			
		}
		CViewer::g_server.SendDataPacket((LPVOID)&responce, sizeof(responce), QUERY_RESPONCE_PARAMETERS, 0);
	}
}

void CViewer::SetSelection(const StreamedMesh* pMesh) {

}

void CViewer::GetSelection(StreamedMesh** pMesh) {

}

void CViewer::OnPacketUpdate(Connection::Packet* pPacket) {
	
	if(!pPacket)
		return;

	const int packetID = pPacket->header.packetID;
	tCharBuffer* pTargetName = (tCharBuffer*)pPacket->pData;

	StreamedMesh* pTargetMesh = NULL;
	if(!Scene::GetGeometry(&pTargetMesh, pTargetName)) {

		pTargetMesh = new StreamedMesh(std::string(pTargetName->buffer));
		if(!pTargetMesh)
			return;

		if(!Scene::InsertGeometry(pTargetMesh)) {
			pTargetMesh->Release();
			delete pTargetMesh;
		}
	}

	switch(packetID) {
	
		case UPDATE_MESHREMOVE:
		{
			Scene::RemoveGeometry(pTargetMesh);
		}
		break;

		case UPDATE_TRANSFORM:
		{
			tMatrix4* pTransform = (tMatrix4*)(pPacket->pData + sizeof(tCharBuffer));
			pTargetMesh->SetTransform(pTransform);
		}
		break;

		case UPDATE_GEOMETRY:
		{
			HeaderStructs::tGeometryHeader* pHeader = (HeaderStructs::tGeometryHeader*)pPacket->pData;
			byte* pGeometry = (pPacket->pData + sizeof(HeaderStructs::tGeometryHeader));
			pTargetMesh->UpdateGeometry(pHeader, pGeometry);
		}
		break;

		case UPDATE_SURFACETEXTURE:
		{
			HeaderStructs::tSurfaceTextureHeader* pHeader = (HeaderStructs::tSurfaceTextureHeader*)pPacket->pData;
			byte* pTexture = (pPacket->pData + sizeof(HeaderStructs::tSurfaceTextureHeader));
			pTargetMesh->UpdateMaterial(pHeader, pTexture);
		} 
		break;

		case UPDATE_PARAMETERS:
		{
			HeaderStructs::tParameterHeader* pHeader = (HeaderStructs::tParameterHeader*)pPacket->pData;
			pTargetMesh->SetShader(std::string(pHeader->shaderName), pHeader->featureSet, pHeader->inputSet);			
		}
		break;
					
		case UPDATE_DEFORMATION:
		{
			HeaderStructs::tDeformationHeader* pHeader = (HeaderStructs::tDeformationHeader*)pPacket->pData;
			pTargetMesh->m_DeformationScale = pHeader->scale;
			pTargetMesh->m_Deformation = pHeader->isActive;			
		}
		break;

		/*case UPDATE_PROCEDURAL:
		{
			tProcedural_ID* pData = (tProcedural_ID*)pPacket->pData;
			StreamedMesh* pSM = NULL;

			if(GetTargetStreamMesh(&pData->meshName, &pSM)) {

				pSM->SetProcedrual(&pData->procedural, &pData->channelName);
			}
		} 
		break;*/

		/*case UPDATE_MESHINSTANCING:
		{

			tProceduralInstancing_ID* pData = (tProceduralInstancing_ID*)pPacket->pData;

			StreamedMesh* pParentMesh = NULL;
			if(!StreamedMesh::FindStreamMesh(&pParentMesh, &pData->parentMeshName)) {
				CViewer::Error("Failed to find mesh for instancing (%s)", &pData->parentMeshName.buffer);
				return;
			}

			StreamedMesh* pChildMesh = NULL;
			if(!StreamedMesh::FindStreamMesh(&pChildMesh, &pData->childMeshName)) {
				CViewer::Error("Failed to find instancing mesh (%s)", &pData->childMeshName.buffer);
				return;
			}

			if(!pParentMesh->AddChildInstancing(pChildMesh, pData->procedural)){
				CViewer::Error("Failed to add instancing to mesh (%s)", &pData->parentMeshName.buffer);
				return;
			}
		}
		break;*/

		}
}

void CViewer::PacketHandler(Connection::Packet* pPacket) {

	if(!pPacket)
		return;

	const int packetID = pPacket->header.packetID;
	if(packetID & PACKET_NETWORK) {
		OnPacketNetwork(pPacket);
	}
	
	if(packetID & PACKET_QUERY) {
		OnPacketQuery(pPacket);
	}
	
	if(packetID & PACKET_UPDATE) {
		OnPacketUpdate(pPacket);
	}

	pPacket->ReleaseHeapData();
}

void CViewer::OnUpdate()
{
	CViewer::g_server.Update();

	std::vector<Connection::Packet> packetList;

	for(uint c = 0; c < CViewer::g_server.ClientCount(); c++) {

		if(CViewer::g_server.GetDataPackets(&packetList,c)) {

			for(std::vector<Connection::Packet>::iterator i = packetList.begin(); i != packetList.end(); i++) {
			
				CViewer::PacketHandler(&(*i));
			}
		}
	}

}

void CViewer::OnInitalize()
{

	CViewer::g_server.Initalize();
	CViewer::g_server.BeginListenForClients();

	Render::Singleton()->Create(CWindow::GetHandle());
	Render::Singleton()->ShowGrid(true);

	Render::Singleton()->SetCameraFrameNavigation(D3DXVECTOR3(-2.4342170, 10.1646695, -2.2819395), -11.69f,0.81f);
	
	GUI::Create();
	Scene::Create();

	StreamedMesh* pTest = NULL;
	Scene::ReadFromDisk("pPlaneShape1", &pTest);
	Scene::InsertGeometry(pTest);

	StreamedMesh* pInstTest = NULL;
	Scene::ReadFromDisk("pConeShape1", &pInstTest);
	pTest->AddChildInstancing(pInstTest, NULL,NULL);
}

void CViewer::OnShutdown()
{	
	Scene::Release();
	Render::Singleton()->Release();
	CViewer::g_server.EndListenForClients();
	CViewer::g_server.Release();

}

void CViewer::OnKeyboardEvent(UINT *pKeyboardState) {

	
	if(pKeyboardState[(UINT)'W'] & KEY_DOWN || pKeyboardState[(UINT)'w'] & KEY_DOWN)
		Render::Singleton()->SetCameraFrameNavigation(D3DXVECTOR3(0,0,.3),0,0);
	
	if(pKeyboardState[(UINT)'S'] & KEY_DOWN || pKeyboardState[(UINT)'s'] & KEY_DOWN)
		Render::Singleton()->SetCameraFrameNavigation(D3DXVECTOR3(0,0,-.3),0,0);
		
	if(pKeyboardState[(UINT)'A'] & KEY_DOWN || pKeyboardState[(UINT)'a'] & KEY_DOWN)
		Render::Singleton()->SetCameraFrameNavigation(D3DXVECTOR3(0.3,0,0),0,0);
			
	if(pKeyboardState[(UINT)'D'] & KEY_DOWN || pKeyboardState[(UINT)'d'] & KEY_DOWN)
		Render::Singleton()->SetCameraFrameNavigation(D3DXVECTOR3(-0.3,0,0),0,0);

	if(pKeyboardState[(UINT)'R'] & KEY_DOWN || pKeyboardState[(UINT)'r'] & KEY_DOWN) {
		Render::Singleton()->ReloadResources();

		/*g_GUI.Release();
		g_GUI.Create();
		g_pClientPanel = g_GUI.CreatePanel();
		g_pObjectPanel = g_GUI.CreatePanel();*/
	}

}

void CViewer::OnMouseEvent(const int dxMouse, const int dyMouse, const int leftButton, const int rightButton) {

	if(leftButton == 1) {

		switch(CViewer::Mode) {

			case CViewer::NAVIGATE_UI:
				CViewer::Mode = CViewer::LastMode;
				break;
			case CViewer::NAVIGATE_FREEVIEW: 
			case CViewer::NAVIGATE_MAYA:
				CViewer::LastMode = CViewer::Mode;
				CViewer::Mode = CViewer::NAVIGATE_UI;
				break;
		}
	}

	switch(CViewer::Mode) {
		
		case CViewer::NAVIGATE_FREEVIEW: {
		
			CWindow::CenterMouse();
			Render::Singleton()->SetCameraFrameNavigation(g_CameraPosition, g_CameraAngles[0],g_CameraAngles[1]);
			g_CameraAngles[0] = (float)(dxMouse) * 0.001f;
			g_CameraAngles[1] = (float)(dyMouse) * 0.001f;
		}
		break;

		case CViewer::NAVIGATE_UI: {

			CWindow::CenterMouse();
			GUI::OnMouseEvent(dxMouse, dyMouse, leftButton, rightButton);
	   }
	   break;
	}
}
void CViewer::OnRenderFrame( void )
{

	/*
	if(CViewer::g_server.ClientCount() == 0 && StreamedMesh::g_streamMeshes.size() > 0) {
		for(UINT i = 0; i < StreamedMesh::g_streamMeshes.size(); i++)
			StreamedMesh::g_streamMeshes[i].Release();
		StreamedMesh::g_streamMeshes.clear();
	}*/

	Render::Singleton()->BeginRendering();
	g_time += 0.05f;

	Scene::Draw();
	GUI::Draw();

	Render::Singleton()->EndRendering();
}
